/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package service.impl;

import debug.Debugger;
import java.util.Date;
import java.util.List;
import java.util.Random;
import pojo.LotteryDrawHistory;
import pojo.LotteryTicket;
import pojo.Pointdispatchhistory;
import pojo.User;
import service.ILotteryService;
import service.ILotteryTicketService;
import service.IPointService;
import service.IUserService;

/**
 *
 * @author beryl
 */
public class LotteryServiceImpl<T extends LotteryDrawHistory> extends ServiceImpl<T>
        implements ILotteryService<T> {

    private static int ticketPrice = 10;
    private static Integer base_prize = 100;
    private static long draw_inteval = 20000;//in millisec
    private static long max_draw_inteval = draw_inteval * 10;//in millisec
    private static boolean enabled = true;
    private static int percentage_miss = 20; //N% chance there is no winner
    private static int percentage_rollover = 50; //N% of current prize added to the next roll
    private static int percentage_price_to_prize = 50;
    private ILotteryTicketService<LotteryTicket> lotteryTicketService;
    private IUserService<User> userService;
    private IPointService<Pointdispatchhistory> pointService;

    public Integer getBase_prize() {
        return base_prize;
    }

    public void setBase_prize(Integer base_prize) {
        LotteryServiceImpl.base_prize = base_prize;
    }

    public long getDraw_inteval() {
        return draw_inteval;
    }

    public void setDraw_inteval(long draw_inteval) {
        LotteryServiceImpl.draw_inteval = draw_inteval;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        LotteryServiceImpl.enabled = enabled;
    }

    public ILotteryTicketService<LotteryTicket> getLotteryTicketService() {
        return lotteryTicketService;
    }

    public void setLotteryTicketService(ILotteryTicketService<LotteryTicket> lotteryTicketService) {
        this.lotteryTicketService = lotteryTicketService;
    }

    public int getPercentage_miss() {
        return percentage_miss;
    }

    public void setPercentage_miss(int percentage_miss) {
        LotteryServiceImpl.percentage_miss = percentage_miss;
    }

    public int getPercentage_price_to_prize() {
        return percentage_price_to_prize;
    }

    public void setPercentage_price_to_prize(int percentage_price_to_prize) {
        LotteryServiceImpl.percentage_price_to_prize = percentage_price_to_prize;
    }

    public int getPercentage_rollover() {
        return percentage_rollover;
    }

    public void setPercentage_rollover(int percentage_rollover) {
        LotteryServiceImpl.percentage_rollover = percentage_rollover;
    }

    public IPointService<Pointdispatchhistory> getPointService() {
        return pointService;
    }

    public void setPointService(IPointService<Pointdispatchhistory> pointService) {
        this.pointService = pointService;
    }

    public int getTicketPrice() {
        return ticketPrice;
    }

    public void setTicketPrice(int ticketPrice) {
        LotteryServiceImpl.ticketPrice = ticketPrice;
    }

    public IUserService<User> getUserService() {
        return userService;
    }

    public void setUserService(IUserService<User> userService) {
        this.userService = userService;
    }

    // if service not enabled: retun null
    // if draw table empty: create a new one
    // if latest draw outdated: create a new draw
    private void createNewDraw(long last_draw_date, Integer prize_rollover) {
        long max_next_draw_date = new Date().getTime() - max_draw_inteval;
        long next_draw_date = (last_draw_date + draw_inteval > max_next_draw_date) ? last_draw_date + draw_inteval : max_next_draw_date;
        T new_draw = (T) new LotteryDrawHistory(null, new Date(next_draw_date),
                base_prize + prize_rollover * percentage_rollover / 100);
        this.getDao().create(new_draw);
    }

    private boolean selectWinner(LotteryDrawHistory draw) {
        Random r = new Random();
        Debugger.msg("selecting winner for draw:" + draw.getDrawid());
        // if a miss happen, ie. int%100 --> [0,99]
        int roll = r.nextInt(100);
        Debugger.msg("Roll: " + roll);
        if ((roll) < percentage_miss) {
            Debugger.msg("no winner by miss %");
            return false;
        } else {
            // fetch all ticket brought for this match
            List<LotteryTicket> tickets = this.getLotteryTicketService().listTicketByDraw(draw.getDrawid());
            if (tickets.isEmpty()) {
                Debugger.msg("no winner because no ticket brought");
                return false;
            }
            // select the winner randomly
            int winner_index = r.nextInt(tickets.size());
            User winner = tickets.get(winner_index).getUser();
            draw.setWinner(winner);
            this.getDao().save((T) draw);
            // create prize history
            this.pointService.create(new Pointdispatchhistory(null, winner, new Date(), draw.getPrize(),
                    "Lottery prize won with Draw ID: " + draw.getDrawid()));
            winner.setPoints(winner.getPoints() + draw.getPrize());
            Debugger.msg("having " + winner.getPoints() + " after winning");
            this.userService.save(winner);
            return true;
        }
    }

    public void buyTicket(User u) {
        LotteryDrawHistory current_draw = listPastDraws(1).get(0);
        // create new ticket
        this.lotteryTicketService.create(new LotteryTicket(null, current_draw, u));
        // create purchase history
        this.pointService.create(new Pointdispatchhistory(null, u, new Date(), -ticketPrice,
                "Lottery ticket brought for Draw with ID: " + current_draw.getDrawid()));
        // subtract user's point
        u.setPoints(u.getPoints() - ticketPrice);
        Debugger.msg("having " + u.getPoints() + " after buying tic");
        this.userService.save(u);
        // increment the prize
        current_draw.setPrize(current_draw.getPrize() + ticketPrice * percentage_price_to_prize / 100);
        this.save((T) current_draw);
    }

    public List<T> listPastDraws(int size) {
        if (!enabled) {
            return null;
        }
        List<T> result = this.getDao().list("select ldh from LotteryDrawHistory ldh order by ldh.drawdate desc",
                0, size);
        if (result.isEmpty()) {
            createNewDraw(new Date().getTime(), 0);
            return listPastDraws(size);
        }
        long last_draw_date = result.get(0).getDrawdate().getTime();
        // the draw is due
        if (last_draw_date < (new Date().getTime())) {
            int rollover = result.get(0).getPrize();
            if (selectWinner(result.get(0))) {
                rollover = 0;
            }
            createNewDraw(last_draw_date, rollover);
            return listPastDraws(size);
        }
        return result;
    }

    public int countTicket(String userid, int drawid) {
        return this.getDao().getTotalCount("select count(t) from LotteryTicket t where t.draw.drawid = " + drawid
                + " and t.user.unikey = '" + userid + "'");
    }
}
